#include "file.h"
#include "util.h"
#include <filesystem>
#include <fstream>

namespace NS_TIEC {
  File::File(const String& path): path_(path) {
  }

  bool File::isExists() {
    return std::filesystem::exists(path_);
  }

  UInt64 File::size() {
    return std::filesystem::file_size(path_);
  }

  String File::getName() {
    std::filesystem::path file_path(path_);
    return file_path.filename().string();
  }

  WString File::getWName() {
    std::filesystem::path file_path(path_);
    return file_path.filename().wstring();
  }

  String File::getExtension() {
    std::filesystem::path file_path(path_);
    return file_path.extension().string();
  }

  WString File::getWExtension() {
    std::filesystem::path file_path(path_);
    return file_path.extension().wstring();
  }

  UInt64 File::lastUpdateTime() {
    auto duration = std::filesystem::last_write_time(path_).time_since_epoch();
    return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
  }

  bool File::mkdir() {
    std::error_code error_code;
    std::filesystem::create_directory(path_, error_code);
    return error_code.value() == 0;
  }

  bool File::mkdirs() {
    std::error_code error_code;
    std::filesystem::create_directories(path_, error_code);
    return error_code.value() == 0;
  }

  File File::getParentFile() {
    std::filesystem::path file_path(path_);
    File file(file_path.parent_path().string());
    return file;
  }

  File File::relativeFile(const String& relative_path) {
    File file(path_ + relative_path);
    return file;
  }

  File File::relativeFile(const WString& relative_path) {
    File file(path_ + toString(relative_path));
    return file;
  }

  String File::getPath() {
    return path_;
  }

  WString File::getWPath() {
    return toWString(path_);
  }

  String File::getAbsolutePath() {
    std::filesystem::path file_path(path_);
    return std::filesystem::absolute(file_path).string();
  }

  WString File::getWAbsolutePath() {
    std::filesystem::path file_path(path_);
    return std::filesystem::absolute(file_path).wstring();
  }

  bool File::renameTo(const File& new_file) {
    std::filesystem::path old_path(path_);
    std::filesystem::path new_path(new_file.path_);
    std::error_code error_code;
    std::filesystem::rename(old_path, new_path, error_code);
    return error_code.value() == 0;
  }

  String File::readString() {
    std::ifstream in(path_);
    std::stringstream buffer;
    if (in) {
      buffer << in.rdbuf();
      std::string contents = buffer.str();
      in.close();
      return contents;
    } else {
      return "";
    }
  }

  WString File::readWString() {
    std::wifstream file(path_);
    if (!file.is_open()) {
      return L"";
    }
    WString content((std::istreambuf_iterator<wchar_t>(file)), (std::istreambuf_iterator<wchar_t>()));
    file.close();
    return content;
  }

  ByteArray File::readBytes() {
    std::ifstream in(path_, std::ios::binary | std::ios::ate);
    if (!in.is_open()) {
      return ByteArray::kEmpty;
    }
    std::streamsize size = in.tellg();
    in.seekg(0, std::ios::beg);
    ByteArray bytes = {new char[size], static_cast<UInt64>(size)};
    if (!in.read(bytes.data, size)) {
      bytes = ByteArray::kEmpty;
    }
    in.close();
    return bytes;
  }

  bool File::writeString(const String& text) {
    std::ofstream out(path_);
    if (!out.is_open()) {
      return false;
    }
    out << text;
    out.close();
    return true;
  }

  bool File::writeWString(const WString& text) {
    std::wofstream out(path_, std::ios::out | std::ios::trunc);
    if (!out.is_open()) {
      return false;
    }
    out << text << std::endl;
    out.close();
    return true;
  }

  bool File::writeBytes(const ByteArray& bytes) {
    std::ofstream file(path_, std::ios::binary);
    if (!file) {
      return false;
    }
    file.write(bytes.data, static_cast<std::streamsize>(bytes.length));
    if (!file.good()) {
      return false;
    }
    file.close();
    return true;
  }

  Uri File::toUri() {
    return Uri::fromString("file://" + getAbsolutePath());
  }
}